package com.qire.antsdeeplink;

import android.content.Intent;
import android.net.Uri;

import com.qire.antscore.common.AssertUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * 深度连接Uri
 * protocol://userInfo@host:port/path?query#fragment
 * 协议://用户信息@主机名:端口/路径?查询#片段
 */
public class DeepLinkUri {
    public static class Builder {

        private final String scheme;
        private String username;
        private String password;
        private final String host;
        private int port = -1;
        private final List<String> pathSegments = new ArrayList<>();
        private final Map<String, String> queryParams = new HashMap<>();
        private String fragment;

        public Builder(String scheme, String host) {
            this.scheme = scheme;
            this.host = host;
        }

        public Builder setUserInfo(String username, String password) {
            if(AssertUtils.notEmpty(username) || AssertUtils.notEmpty(password)) {
                this.username = username;
                this.password = password;
            }
            return this;
        }

        /**
         * 设置端口号
         * @param port 范围内[1 . . 65535]
         * @return
         */
        public Builder setPort(int port) {
            this.port = port > 0 ? port : -1;
            return this;
        }

        public Builder addPathSegment(String pathSegment) {
            this.pathSegments.add(pathSegment);
            return this;
        }

        public Builder addQueryParam(String queryName, String queryValue) {
            if(AssertUtils.notEmpty(queryName) && AssertUtils.notEmpty(queryValue)) {
                this.queryParams.put(queryName, queryValue);
            }
            return this;
        }

        /**
         * 设置锚点片段，例如："#fragment"
         * @param fragment
         * @return
         */
        public Builder setFragment(String fragment) {
            this.fragment = fragment;
            return this;
        }

        private Uri buildUri() {
            return null;
        }

        public DeepLinkUri build() {
            return new DeepLinkUri(buildUri(), scheme, username, password, host, port, pathSegments, queryParams, fragment);
        }
    }

    public static DeepLinkUri parseFrom(Intent deepLinkIntent) {
        if(deepLinkIntent == null) {
            return null;
        }
        return parseFrom(deepLinkIntent.getData());
    }
    public static DeepLinkUri parseFrom(Uri deepLinkUri) {
        if(deepLinkUri == null) {
            return null;
        }

        // 解析 scheme
        String scheme = deepLinkUri.getScheme();

        // 解析 username 和 password
        String[] userInfo;
        if(AssertUtils.notEmpty(deepLinkUri.getUserInfo())) {
            userInfo = deepLinkUri.getUserInfo().split(":");
        } else {
            userInfo = new String[]{"", ""};
        }
        String username = userInfo[0];
        String password = userInfo[1];

        // 解析 host 主机名(域名)
        String host = deepLinkUri.getHost();

        // 解析 port 端口号
        int port = deepLinkUri.getPort();

        // 解析分段路径
        List<String> pathSegments = deepLinkUri.getPathSegments();

        // 解析请求参数
        Map<String, String> queryParams = new HashMap<>();
        for(String name : deepLinkUri.getQueryParameterNames()) {
            queryParams.put(name, deepLinkUri.getQueryParameter(name));
        }

        // 解析片段锚点
        String fragment = deepLinkUri.getFragment();

        return new DeepLinkUri(deepLinkUri, scheme, username, password, host, port, pathSegments, queryParams, fragment);
    }

    /** 协议://用户信息@主机名:端口/路径?查询#片段 <br /> scheme://username:password@host:port/pathSegments?queryParams#fragment*/
    private final Uri sourceUri;

    /** 协议，如 "http" or "https".. */
    private final String scheme;

    /** Decoded username. */
    private final String username;

    /** Decoded password. */
    private final String password;

    /** 主机名，域名. */
    private final String host;

    /** 80、443或用户指定的端口。范围内[1 . . 65535]。*/
    private final int port;

    /**
     * 分段路径的列表。此列表始终包含至少一个元素，可以是空字符串。
     * 每个段都以前导 "/" 格式化，因此，如果路径段为["a", "b", ""]，则编码路径将为"/a/b"。
     */
    private final List<String> pathSegments = new ArrayList();

    /** Decoded queryParams. 查询参数，格式如: "?name=summer&page=1" */
    private final Map<String, String> queryParams;

    /** Decoded fragment. 片段，web中用于定位锚点，此处可以扩展为页面某个位置或子页，格式如: "#HomeFragment" */
    private final String fragment;

    private DeepLinkUri(Uri sourceUri, String scheme, String username, String password, String host, int port,
                        List<String> pathSegments, Map<String, String> queryParams, String fragment) {
        this.sourceUri = sourceUri;
        this.scheme = scheme;
        this.username = username;
        this.password = password;
        this.host = host;
        this.port = port;
        if(AssertUtils.isEmpty(pathSegments)) {
            this.pathSegments.add("");
        } else {
            this.pathSegments.addAll(pathSegments);
        }
        this.queryParams = queryParams;
        this.fragment = fragment;
    }

    public String fullPathUseToMatch() {
        return "/" + host() + fullPath();
    }

    public String scheme() {
        return scheme;
    }

    public String host() {
        return host;
    }

    public String fullPath() {
        if(AssertUtils.nonNull(sourceUri)) {
            return sourceUri.getPath();
        }

        StringBuilder pathBuilder = new StringBuilder();
        for(String pathSegment : pathSegments) {
            pathBuilder.append("/").append(pathSegment);
        }

        return pathBuilder.toString();
    }

    public String firstPath() {
        return pathSegments.get(0);
    }

    public String lastPath() {
        if(AssertUtils.nonNull(sourceUri)) {
            return sourceUri.getLastPathSegment();
        }
        return pathSegments.get(pathSegments.size()-1);
    }

    public String[] queryNames() {
        if(AssertUtils.isEmpty(queryParams)) {
            return new String[0];
        }
        return queryParams.keySet().toArray(new String[0]);
    }

    public String queryValue(String queryName) {
        return queryParams.get(queryName);
    }


    public String encodedUsername() {
        if(AssertUtils.isEmpty(username)) {
            return "";
        }
        // todo: 需要增加 encoded 加密处理
        return username;
    }
    public String encodedPassword() {
        if(AssertUtils.isEmpty(password)) {
            return "";
        }
        // todo: 需要增加 encoded 加密处理
        return password;
    }

    public List<String> encodedPathSegments() {
        if(AssertUtils.isEmpty(pathSegments)) {
            return new ArrayList<>();
        }
        // todo: 需要增加 encoded 加密处理
        return pathSegments;
    }

    public Map<String, String> encodedQueryParams() {
        if(AssertUtils.isEmpty(queryParams)) {
            return new HashMap<>();
        }
        // todo: 需要增加 encoded 加密处理
        return queryParams;
    }

    public String encodedFragment() {
        if(AssertUtils.isEmpty(fragment)) {
            return null;
        }
        // todo: 需要增加 encoded 加密处理
        return fragment;
    }

    int effectivePort() {
        return port != -1 ? port : defaultPort(scheme);
    }

    /**
     * 如果 scheme.equals("http") 返回 80，如果 scheme.equals("https") 返回 443，否则返回-1。
     */
    static int defaultPort(String scheme) {
        if (scheme.equals("http")) {
            return 80;
        } else if (scheme.equals("https")) {
            return 443;
        } else {
            return -1;
        }
    }

    static void pathSegmentsToString(StringBuilder out, List<String> pathSegments) {
        for (int i = 0, size = pathSegments.size(); i < size; i++) {
            out.append('/');
            out.append(pathSegments.get(i));
        }
    }

    static void queryParamsToQueryString(StringBuilder out, Map<String, String> queryParams) {
        Set<String> queryNames = queryParams.keySet();
        int i = 0;
        for(String queryName : queryNames) {
            String name = queryName;
            String value = queryParams.get(queryName);
            if (i++ > 0) out.append('&');
            out.append(name);
            if (value != null) {
                out.append('=');
                out.append(value);
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        result.append(scheme);
        result.append("://");

        final String encodedUsername = encodedUsername();
        final String encodedPassword = encodedPassword();
        if (AssertUtils.notEmpty(encodedUsername) || AssertUtils.notEmpty(encodedPassword)) {
            result.append(encodedUsername);
            if (AssertUtils.notEmpty(encodedPassword)) {
                result.append(':');
                result.append(encodedPassword);
            }
            result.append('@');
        }

        if (host.indexOf(':') != -1) {
            // Host is an IPv6 address.
            result.append('[');
            result.append(host);
            result.append(']');
        } else {
            result.append(host);
        }

        final int effectivePort = effectivePort();
        if (effectivePort != defaultPort(scheme)) {
            result.append(':');
            result.append(effectivePort);
        }

        pathSegmentsToString(result, encodedPathSegments());

        final Map<String, String> encodedQueryParams = encodedQueryParams();
        if (AssertUtils.notEmpty(encodedQueryParams)) {
            result.append('?');
            queryParamsToQueryString(result, encodedQueryParams);
        }

        final String encodedFragment = encodedFragment();
        if (AssertUtils.notEmpty(encodedFragment)) {
            result.append('#');
            result.append(encodedFragment);
        }

        return result.toString();
    }

} 
